package semantic_compute.relation_extraction.yago_matcher;

import com.carrotsearch.randomizedtesting.LifecycleScope;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientEdge;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
import com.tinkerpop.blueprints.impls.orient.OrientVertex;
import common.dao.orient.OrientGraphDao;

import javafx.util.Pair;
import knowlwdge_graph.knowledge_etl.yago_etl.SchemaDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.tools.cmd.gen.AnyVals;
import semantic_compute.common.yago_helper.YagoLuceneQueryer;
import semantic_compute.common.yago_helper.YagoQueryHelper;
import semantic_compute.relation_extraction.schema.RelationMatchResult;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by julianzliu on 4/23/2017.
 */
public class YagoRelationMatcher {

    static final Logger logger = LoggerFactory.getLogger( YagoRelationMatcher.class );

    private OrientGraphDao orientGraphDao;
    private OrientGraph orientGraph;

    //private YagoLuceneQueryer luceneQueryer;


    public YagoRelationMatcher(){

        this.orientGraphDao = new OrientGraphDao();
        this.orientGraph = this.orientGraphDao.getGraph();

        //this.luceneQueryer = new YagoLuceneQueryer();

    }



    public List<Pair<String, Long>>  matchSingleRelationByPatty(String relation){
        String sql = String.format(
                "select yago_id,count(*) as count from (" + "\n" +
                        "    select expand(in()) from (" + "\n" +
                        "        select *,$score from YAGO_CONCEPT_LABEL_CLASS where yago_label LUCENE '%s'" + "\n" +
                        "    ) ) group by yago_id order by count desc;"
                , relation
        );


        logger.info("[getIndgreeSortedResult] sql = \n{}" , sql);
        Iterable<Vertex> vertices = (Iterable<Vertex>) this.orientGraph.command(
                new OCommandSQL(sql )
        ).execute();

        List<Pair<String, Long>> rankedList = new ArrayList<>();

        for(Vertex row: vertices){
            String yago_id = row.getProperty("yago_id");
            Long count = row.getProperty("count");
            rankedList.add( new Pair(yago_id, count) );
        }

        return rankedList;
    }

    public List<Pair<String, Long>>  matchSingleRelation(String relation){
        relation = YagoQueryHelper.phraseNoneEngToQuery(relation);
        String sql = String.format(
                "select yago_id,out().asList().size() as count from (" + "\n" +
                        "select expand(in()) from (" + "\n" +
                                "select *,$score,in() from YAGO_RELATION_GLOSS_CLASS where yago_label LUCENE '%s' " + "\n" +
                    "))"
                , relation
        );
        logger.info("[matchSingleRelation] sql = \n{}" , sql);
        Iterable<Vertex> vertices = (Iterable<Vertex>) this.orientGraph.command(
                new OCommandSQL(sql )
        ).execute();

        List<Pair<String, Long>> rankedList = new ArrayList<>();

        for(Vertex row: vertices){
            String yago_id = row.getProperty("yago_id");
            Integer count_int = row.getProperty("count");
            Long count = Long.valueOf(count_int.longValue());
            rankedList.add( new Pair(yago_id, count) );
        }

        return rankedList;
    }

    private String genNonLabelQuery(){
        String[] label_array = SchemaDefinition.YAGO_LABEL_EDGE.label_array;
        String query = "(yago_label!='rdf:type' ";
        for(Integer i = 0; i< label_array.length; i++){
            query += " and yago_label != '" + label_array[i] + "' " ;
        }
        query += " )";
        return query;
    }

    private String genEdgeSetQuery(List<String> edgeSet){
        String query = "(";
        if(edgeSet.size() > 0) query += " yago_label='" + edgeSet.get(0) + "' ";
        for(Integer i = 1; i < edgeSet.size(); i++)
            query += " or yago_label='" + edgeSet.get(i) + "' ";
        query += ")";
        return query;
    }


    public List<RelationMatchResult>  matchLengthOnePath(String vertex, List<String> edgeSet1){
        List<RelationMatchResult> results = new ArrayList<>();

        if(edgeSet1.size() == 0 ){
            System.out.println("[matchLengthOnePath] \n\n size of edgeset1 = " + edgeSet1.size()  + "\n\n");
            return results;
        }
        String query1 = genEdgeSetQuery( edgeSet1 );
        String sql  = String.format(
                "match { class : V , as : start, where: (yago_id='%s')} " + "\n" +
                        ".bothE() { as: edge1, where: (%s) } " + "\n" +
                        ".bothV() { as: end, where:($matched.start != $currentMatch)} " + "\n" +
                        "return start,edge1,end " + "\n"
                , vertex, query1
        );
        logger.info("[matchLengthTwoPath] sql = \n{}" , sql);
        Iterable<Vertex>  records = this.orientGraph.command(
                new OCommandSQL(sql )
        ).execute();


        for(Vertex record: records){
            RelationMatchResult result = new RelationMatchResult();
            result.start = record.getProperty("start");
            OrientEdge edge1 = record.getProperty("edge1");
            result.edges.add(edge1);
            OrientVertex end = record.getProperty("end");
            result.vetexes.add(end);
            System.out.println( record + " : " + edge1.getProperty("yago_label") + " -> " + end.getProperty("yago_label") );

            results.add( result );
        }
        return results;

    }

    public List<RelationMatchResult> matchLengthTwoPath(String vertex, List<String> edgeSet1, List<String> edgeSet2){
        List<RelationMatchResult> results = new ArrayList<>();

        if(edgeSet1.size() == 0 || edgeSet2.size() == 0){
            System.out.println("[matchLengthTwoPath] \n\n size of edgeset1 = " + edgeSet1.size() + " , size of edgeset2 = " + edgeSet2.size() + "\n\n");
            return results;
        }
        /*
        Match 语法
         */
        /*
        match { class : V , as : start, where: (@rid=#52:49108)}
        .bothE() { as: edge1, where: (yago_label = '<isMarriedTo>' or yago_label = '<wasBornIn>') }
        .bothV() { as: mid, where:($matched.start != $currentMatch)}
        .bothE() { as: edge2, where:( (yago_label ='<wasBornIn>') and($matched.edge1 != $currentMatch)) }
        .bothV(){ as : end, where:($matched.start != $currentMatch and $matched.mid != $currentMatch) }
        return start,edge1,mid,edge2 ,end
         */
        String query1 = genEdgeSetQuery( edgeSet1 );
        String query2 = genEdgeSetQuery( edgeSet2 );
        String sql  = String.format(
                        "match { class : V , as : start, where: (yago_id='%s')} " + "\n" +
                        ".bothE() { as: edge1, where: (%s) } " + "\n" +
                        ".bothV() { as: mid, where:($matched.start != $currentMatch)} " + "\n" +
                        ".bothE() { as: edge2, where:( (%s) and($matched.edge1 != $currentMatch)) } " + "\n" +
                        ".bothV(){ as : end, where:($matched.start != $currentMatch and $matched.mid != $currentMatch) } " + "\n" +
                        "return start,edge1,mid,edge2 ,end " + "\n"
                , vertex, query1, query2
        );
        logger.info("[matchLengthTwoPath] sql = \n{}" , sql);
        Iterable<Vertex>  records = this.orientGraph.command(
                new OCommandSQL(sql )
        ).execute();

        for(Vertex record: records){
            RelationMatchResult result = new RelationMatchResult();
            result.start = record.getProperty("start");
            OrientEdge edge1 = record.getProperty("edge1");
            result.edges.add(edge1);
            OrientEdge edge2 = record.getProperty("edge2");
            result.edges.add(edge2);
            OrientVertex mid = record.getProperty("mid");
            result.vetexes.add(mid);
            OrientVertex end = record.getProperty("end");
            result.vetexes.add(end);
            System.out.println( record + " : " + edge1.getProperty("yago_label") + " -> " + mid.getProperty("yago_label") + " -> " + edge2.getProperty("yago_label") );

            results.add( result );
        }
        return results;
    }




    /***********************************************************************
     *
     */
    public static void testLengthOneEdge( YagoRelationMatcher relationMatcher){
        List<String> edgeSet1 = new ArrayList<>();
        edgeSet1.add("<isMarriedTo>");
        edgeSet1.add("<diedIn>");
        relationMatcher.matchLengthOnePath("<Barack_Obama>" ,edgeSet1);
    }
    public static void testLengthTwoEdge( YagoRelationMatcher relationMatcher){
        List<String> edgeSet1 = new ArrayList<>();
        edgeSet1.add("<isMarriedTo>");
        edgeSet1.add("<diedIn>");
        List<String> edgeSet2 = new ArrayList<>();
        edgeSet2.add("<wasBornIn>");
        relationMatcher.matchLengthTwoPath("<Barack_Obama>" ,edgeSet1, edgeSet2);
    }


    public static void main(String[] args){
        String phrase = "was graduated from";
        if(args.length > 0){
            phrase = "";
            for(String token:args) phrase += " " + token;
        }

        YagoRelationMatcher relationMatcher = new YagoRelationMatcher();
        relationMatcher.matchSingleRelationByPatty( phrase );

        System.out.println("\n\n-------------------------\n\n");

        testLengthOneEdge(relationMatcher);

        System.out.println("\n\n-------------------------\n\n");

        testLengthTwoEdge(relationMatcher);

        System.out.println("\n\n-------------------------\n\n");
    }



}
